using System.Web.Security;
using System.Configuration.Provider;
using System.Collections.Specialized;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Diagnostics;
using System.Web;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using LMSData;

namespace SubSonic.Web
{

    public sealed class SubSonicMembershipProvider : MembershipProvider
    {
        
        //
        // Global connection string, generated password length, generic exception message, event log info.
        //

        private int newPasswordLength = 8;
        private string eventSource = "SubSonicMembershipProvider";
        private string eventLog = "Application";
        private string exceptionMessage = "An exception occurred. Please check the Event Log.";
        //private string tableName = "User";
        private string connectionString;

        private const string encryptionKey = "AE09F72B007CAAB5";

        //
        // If false, exceptions are thrown to the caller. If true,
        // exceptions are written to the event log.
        //

        private bool pWriteExceptionsToEventLog;

        public bool WriteExceptionsToEventLog
        {
            get { return pWriteExceptionsToEventLog; }
            set { pWriteExceptionsToEventLog = value; }
        }


        //
        // System.Configuration.Provider.ProviderBase.Initialize Method
        //

        public override void Initialize(string name, NameValueCollection config)
        {
            //
            // Initialize values from web.config.
            //

            if (config == null)
                throw new ArgumentNullException("config");

            if (name == null || name.Length == 0)
                name = "SubSonicMembershipProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "SubSonic Membership provider");
            }

            // Initialize the abstract base class.
            base.Initialize(name, config);

            pApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            pMaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            pPasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
            pMinRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
            pMinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
            pPasswordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
            pEnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
            pEnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
            pRequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
            pRequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
            pWriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));

            string temp_format = config["passwordFormat"];
            if (temp_format == null)
            {
                temp_format = "Clear";
            }

            switch (temp_format)
            {
                case "Hashed":
                    pPasswordFormat = MembershipPasswordFormat.Hashed;
                    break;
                case "Encrypted":
                    pPasswordFormat = MembershipPasswordFormat.Encrypted;
                    break;
                case "Clear":
                    pPasswordFormat = MembershipPasswordFormat.Clear;
                    break;
                default:
                    throw new ProviderException("Password format not supported.");
            }

            //
            // Initialize Connection.
            //

            ConnectionStringSettings ConnectionStringSettings =
              ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

            if (ConnectionStringSettings == null || ConnectionStringSettings.ConnectionString.Trim() == "")
            {
                throw new ProviderException("Connection string cannot be blank.");
            }

            connectionString = ConnectionStringSettings.ConnectionString;


        }


        //
        // A helper function to retrieve config values from the configuration file.
        //

        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }


        //
        // System.Web.Security.MembershipProvider properties.
        //


        private string pApplicationName;
        private bool pEnablePasswordReset;
        private bool pEnablePasswordRetrieval;
        private bool pRequiresQuestionAndAnswer;
        private bool pRequiresUniqueEmail;
        private int pMaxInvalidPasswordAttempts;
        private int pPasswordAttemptWindow;
        private MembershipPasswordFormat pPasswordFormat;

        public override string ApplicationName
        {
            get { return pApplicationName; }
            set { pApplicationName = value; }
        }

        public override bool EnablePasswordReset
        {
            get { return pEnablePasswordReset; }
        }


        public override bool EnablePasswordRetrieval
        {
            get { return pEnablePasswordRetrieval; }
        }


        public override bool RequiresQuestionAndAnswer
        {
            get { return pRequiresQuestionAndAnswer; }
        }


        public override bool RequiresUniqueEmail
        {
            get { return pRequiresUniqueEmail; }
        }


        public override int MaxInvalidPasswordAttempts
        {
            get { return pMaxInvalidPasswordAttempts; }
        }


        public override int PasswordAttemptWindow
        {
            get { return pPasswordAttemptWindow; }
        }


        public override MembershipPasswordFormat PasswordFormat
        {
            get { return pPasswordFormat; }
        }

        private int pMinRequiredNonAlphanumericCharacters;

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return pMinRequiredNonAlphanumericCharacters; }
        }

        private int pMinRequiredPasswordLength;

        public override int MinRequiredPasswordLength
        {
            get { return pMinRequiredPasswordLength; }
        }

        private string pPasswordStrengthRegularExpression;

        public override string PasswordStrengthRegularExpression
        {
            get { return pPasswordStrengthRegularExpression; }
        }

        //
        // System.Web.Security.MembershipProvider methods.
        //

        //
        // MembershipProvider.ChangePassword
        //

        public override bool ChangePassword(string username, string oldPwd, string newPwd)
        {

            
            if (!ValidateUser(username, oldPwd))
                return false;


            ValidatePasswordEventArgs args =
              new ValidatePasswordEventArgs(username, newPwd, true);

            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Change password canceled due to new password validation failure.");

            User usr = new User(User.Columns.Username, username);

            try
            {

                
                if (usr.IsLoaded)
                {
                    usr.Password = EncodePassword(newPwd);
                    usr.Save();
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ChangePassword");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            if (!usr.IsDirty)
            {
                return true;
            }

            return false;
        }



        //
        // MembershipProvider.ChangePasswordQuestionAndAnswer
        //

        public override bool ChangePasswordQuestionAndAnswer(string username,
                      string password,
                      string newPwdQuestion,
                      string newPwdAnswer)
        {
            bool Changed = false;
            if (!ValidateUser(username, password))
                return false;

            
            try
            {

                User usr = new User(User.Columns.Username, username);
                if (usr != null)
                {
                    usr.PasswordQuestion = newPwdQuestion;
                    usr.PasswordAnswer = EncodePassword(newPwdAnswer);
                    usr.Save();
                    Changed = true;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ChangePasswordQuestionAndAnswer");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }


            return Changed;
        }



        //
        // MembershipProvider.CreateUser
        //

        public override MembershipUser CreateUser(string username,
                 string password,
                 string email,
                 string passwordQuestion,
                 string passwordAnswer,
                 bool isApproved,
                 object providerUserKey,
                 out MembershipCreateStatus status)
        {
            ValidatePasswordEventArgs args =
              new ValidatePasswordEventArgs(username, password, true);

            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (passwordQuestion == null) passwordQuestion = string.Empty;
            if (passwordAnswer == null) passwordAnswer = string.Empty;

            if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            MembershipUser u = GetUser(username, false);

            if (u == null)
            {
                DateTime createDate = DateTime.Now;

                if (providerUserKey == null)
                {
                    providerUserKey = Guid.NewGuid();
                }
                else
                {
                    if (!(providerUserKey is Guid))
                    {
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }
                }
                User usr = new User();

                try
                {
                    usr.Username = username;
                    usr.Password = EncodePassword(password);
                    usr.Email = email;
                    usr.PasswordQuestion = passwordQuestion;
                    usr.PasswordAnswer = passwordAnswer == null ? null : EncodePassword(passwordAnswer);
                    usr.IsApproved = isApproved;
                    usr.Comment = String.Empty;
                    usr.CreationDate = createDate;
                    usr.LastPasswordChangedDate = createDate;
                    usr.LastActivityDate = createDate;
                    usr.IsLockedOut = false;
                    usr.LastLockedOutDate = createDate;
                    usr.FailedPasswordAnswerAttemptCount = 0;
                    usr.FailedPasswordAnswerAttemptWindowStart = createDate;
                    usr.FailedPasswordAttemptCount = 0;
                    usr.FailedPasswordAttemptWindowStart = createDate;
                    usr.Save();
                    
                    if (!usr.IsDirty)
                    {
                        status = MembershipCreateStatus.Success;
                    }
                    else
                    {
                        status = MembershipCreateStatus.UserRejected;
                    }
                }
                catch (SqlException e)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(e, "CreateUser");
                    }

                    status = MembershipCreateStatus.ProviderError;
                }
                

                return GetUser(username, false);
            }
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }


            return null;
        }



        //
        // MembershipProvider.DeleteUser
        //

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
           

            try
            {
                User usr = new User(User.Columns.Username, username);
                User.Destroy(usr.UserID);

            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "DeleteUser");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return true;
        }



        //
        // MembershipProvider.GetAllUsers
        //

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            UserCollection usrs = new UserCollection();

            MembershipUserCollection users = new MembershipUserCollection();

            try
            {
                usrs.Load();
                totalRecords = usrs.Count;

                if (totalRecords <= 0) { return users; }

                users = PageUsers(usrs, pageIndex, pageSize);

            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllUsers");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return users;
        }

        //
        // MembershipProvider.GetNumberOfUsersOnline
        //

        public override int GetNumberOfUsersOnline()
        {

            TimeSpan onlineSpan = new TimeSpan(0, System.Web.Security.Membership.UserIsOnlineTimeWindow, 0);
            DateTime compareTime = DateTime.Now.Subtract(onlineSpan);

            
            SubSonic.Query qry = new Query(User.Schema);
            qry.AddWhere(User.Columns.LastActivityDate,SubSonic.Comparison.GreaterThan,compareTime);
            qry.SelectList = "Count(*)"; 
            
            
            int numOnline = 0;

            try
            {
                numOnline = Convert.ToInt32(qry.ExecuteScalar());
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetNumberOfUsersOnline");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return numOnline;
        }



        //
        // MembershipProvider.GetPassword
        //

        public override string GetPassword(string username, string answer)
        {
            if (!EnablePasswordRetrieval)
            {
                throw new ProviderException("Password Retrieval Not Enabled.");
            }

            if (PasswordFormat == MembershipPasswordFormat.Hashed)
            {
                throw new ProviderException("Cannot retrieve Hashed passwords.");
            }

            

            string password = "";
            string passwordAnswer = "";

            try
            {
                User usr = new User(User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    if ((bool)usr.IsLockedOut)
                        throw new MembershipPasswordException("The supplied user is locked out.");
                    password = usr.Password;
                    passwordAnswer = usr.PasswordAnswer;
                }
        		else
			    {
				    throw new MembershipPasswordException("The supplied user name is not found.");
			    }
			
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetPassword");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }



            if (RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
            {
                UpdateFailureCount(username, "passwordAnswer");

                throw new MembershipPasswordException("Incorrect password answer.");
            }


            if (PasswordFormat == MembershipPasswordFormat.Encrypted)
            {
                password = UnEncodePassword(password);
            }

            return password;
        }



        //
        // MembershipProvider.GetUser(string, bool)
        //

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            

            MembershipUser u = null;

            try
            {
                User usr = new User(User.Columns.Username, username);
                if (usr.IsLoaded && usr.Username != null)
                {
                    
                    u = GetMembershipUserFromUser(usr);
                    if (userIsOnline)
                    {
                        usr.LastActivityDate = DateTime.Now;
                        usr.Save();
                    }
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(Object, Boolean)");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }


            return u;
        }


        //
        // MembershipProvider.GetUser(object, bool)
        //

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            
            MembershipUser u = null;

            try
            {
                User usr = new User(providerUserKey);
                if (usr.IsLoaded)
                {
                    u = GetMembershipUserFromUser(usr);
                    if (userIsOnline)
                    {
                        usr.LastActivityDate = DateTime.Now;
                        usr.Save();
                    }
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(Object, Boolean)");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
 

            return u;
        }


        //
        // GetUserFromReader
        //    A helper function that takes the current row from the SqlDataReader
        // and hydrates a MembershiUser from the values. Called by the 
        // MembershipUser.GetUser implementation.
        //

        private MembershipUser GetMembershipUserFromUser(LMSData.User usr)
        {
            /*object providerUserKey = usr.UserID;
            string username = usr.Username
            string email = reader.IsDBNull(2)                      ? "" :reader.GetString(2);
            string passwordQuestion = reader.IsDBNull(3)           ? "" : reader.GetString(3);
            string comment = reader.IsDBNull(4)                    ? "" : reader.GetString(4);
            bool isApproved = reader.IsDBNull(5)                   ? false : reader.GetBoolean(5);
            bool isLockedOut = reader.IsDBNull(6)                  ? false : reader.GetBoolean(6);
            DateTime creationDate = reader.IsDBNull(7)             ? DateTime.Now : reader.GetDateTime(7);
            DateTime lastLoginDate = reader.IsDBNull(8)            ? DateTime.Now : reader.GetDateTime(8);
            DateTime lastActivityDate = reader.IsDBNull(9)         ? DateTime.Now : reader.GetDateTime(9);
            DateTime lastPasswordChangedDate = reader.IsDBNull(10) ? DateTime.Now : reader.GetDateTime(10);
            DateTime lastLockedOutDate = reader.IsDBNull(11)       ? DateTime.Now : reader.GetDateTime(11);
             * */

            MembershipUser u = new MembershipUser(this.Name,
                                                  usr.Username,
                                                  (object)usr.UserID,
                                                  usr.Email,
                                                  usr.PasswordQuestion,
                                                  usr.Comment,
                                                  (bool)usr.IsApproved,
                                                  (bool)usr.IsLockedOut,
                                                  checkNull(usr.CreationDate),
                                                  checkNull(usr.LastLoginDate),
                                                  checkNull(usr.LastActivityDate),
                                                  checkNull(usr.LastPasswordChangedDate),
                                                  checkNull(usr.LastLockedOutDate));

            return u;
        }

        private DateTime checkNull(DateTime? dt)
        {
            if (dt == null)
                dt = DateTime.Now;
            return (DateTime)dt;

        }

        //
        // MembershipProvider.UnlockUser
        //

        public override bool UnlockUser(string username)
        {

            bool Unlocked = false;
            try
            {
                User usr = new User(User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    usr.IsLockedOut = false;
                    usr.LastLockedOutDate = DateTime.Now;
                    usr.Save();
                    Unlocked = true;
                }
            }
            catch (SqlException e)
            {
                Unlocked = false;
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UnlockUser");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }


            return Unlocked;
        }


        //
        // MembershipProvider.GetUserNameByEmail
        //

        public override string GetUserNameByEmail(string email)
        {
            

            string username = "";

           


            try
            {
                User usr = new User(User.Columns.Email, email);
                if (usr.IsLoaded)
                {
                    username = usr.Username;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUserNameByEmail");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            

            if (username == null)
                username = "";

            return username;
        }

        //
        // MembershipProvider.ResetPassword
        //

        public override string ResetPassword(string username, string answer)
        {
            bool Reset = false;
            if (!EnablePasswordReset)
            {
                throw new NotSupportedException("Password reset is not enabled.");
            }

            if (answer == null && RequiresQuestionAndAnswer)
            {
                UpdateFailureCount(username, "passwordAnswer");

                throw new ProviderException("Password answer required for password reset.");
            }

            string newPassword =
              System.Web.Security.Membership.GeneratePassword(newPasswordLength, MinRequiredNonAlphanumericCharacters);


            ValidatePasswordEventArgs args =
              new ValidatePasswordEventArgs(username, newPassword, true);

            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Reset password canceled due to password validation failure.");
           

            string passwordAnswer = "";

            try
            {
                User usr = new User(User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    if((bool)usr.IsLockedOut)
                        throw new MembershipPasswordException("The supplied user is locked out.");
                    passwordAnswer = usr.PasswordAnswer;

                    if (RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
                    {
                        UpdateFailureCount(username, "passwordAnswer");

                        throw new MembershipPasswordException("Incorrect password answer.");
                    }
                    usr.Password = EncodePassword(newPassword);
                    usr.LastPasswordChangedDate = DateTime.Now;
                    usr.Save();
                    Reset = true;

                }
                else
                {
                    throw new MembershipPasswordException("The supplied user name is not found.");
                }
            }

            
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ResetPassword");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            
            if (Reset)
            {
                return newPassword;
            }
            else
            {
                throw new MembershipPasswordException("User not found, or user is locked out. Password not Reset.");
            }
        }


        //
        // MembershipProvider.UpdateUser
        //

        public override void UpdateUser(MembershipUser user)
        {
            User usr = new User();
            try
            {
                usr.LoadByParam(User.Columns.Username, user.UserName);
                if (usr.IsLoaded)
                {
                    usr.Email = user.Email;
                    usr.Comment = user.Comment;
                    usr.IsApproved = user.IsApproved;
                    usr.Username = user.UserName;
                    usr.Save();
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateUser");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

        }


        //
        // MembershipProvider.ValidateUser
        //

        public override bool ValidateUser(string username, string password)
        {
            bool isValid = false;
            bool isApproved = false;
            User usr = new User();
            string pwd = "";

            try
            {
                usr.LoadByParam(User.Columns.Username, username);
                if (usr.IsLoaded && usr.Username != null) 
                {
					pwd = usr.Password;
					isApproved = (bool)usr.IsApproved;
                    if (CheckPassword(password, pwd))
                    {
                        if (isApproved)
                        {
                            isValid = true;
                            usr.LastLoginDate = DateTime.Now;
                            usr.LastActivityDate = DateTime.Now;
                            usr.Save();
                        }
                    }
                    else
                    {
                        UpdateFailureCount(username, "password");
                    }

				}
				else
				{
					return false;
				}
				
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ValidateUser");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return isValid;
        }


        //
        // UpdateFailureCount
        //   A helper method that performs the checks and updates associated with
        // password failure tracking.
        //

        private void UpdateFailureCount(string username, string failureType)
        {

            
            DateTime windowStart = new DateTime();
            int failureCount = 0;

            try
            {
                User usr = new User(User.Columns.Username, username);
                if (usr.IsLoaded)
                {
                    if (failureType == "password")
                    {
                        failureCount = (int)usr.FailedPasswordAttemptCount;
                        windowStart = (DateTime)usr.FailedPasswordAttemptWindowStart;
                    }

                    if (failureType == "passwordAnswer")
                    {
                        failureCount = (int)usr.FailedPasswordAnswerAttemptCount;
                        windowStart = (DateTime)usr.FailedPasswordAnswerAttemptWindowStart;
                    }
                    DateTime windowEnd = windowStart.AddMinutes(PasswordAttemptWindow);

                    if (failureCount == 0 || DateTime.Now > windowEnd)
                    {
                        // First password failure or outside of PasswordAttemptWindow. 
                        // Start a new password failure count from 1 and a new window starting now.

                        if (failureType == "password")
                        {
                            usr.FailedPasswordAttemptCount = 1;
                            usr.FailedPasswordAttemptWindowStart = DateTime.Now;
                        }
                        if (failureType == "passwordAnswer")
                        {
                            usr.FailedPasswordAnswerAttemptCount = 1;
                            usr.FailedPasswordAnswerAttemptWindowStart = DateTime.Now;
                        }
                        usr.Save();

                        if (usr.IsDirty)
                            throw new ProviderException("Unable to update failure count and window start.");
                    }
                    else
                    {
                        if (failureCount++ >= MaxInvalidPasswordAttempts)
                        {
                            // Password attempts have exceeded the failure threshold. Lock out
                            // the user.

                            usr.IsLockedOut = true;
                            usr.LastLockedOutDate = DateTime.Now;
                            usr.Save();


                            if (usr.IsDirty)
                                throw new ProviderException("Unable to lock out user.");
                        }
                        else
                        {
                            // Password attempts have not exceeded the failure threshold. Update
                            // the failure counts. Leave the window the same.

                            if (failureType == "password")
                            {
                                usr.FailedPasswordAttemptCount = failureCount;
                            }

                            if (failureType == "passwordAnswer")
                            {
                                usr.FailedPasswordAnswerAttemptCount = failureCount;
                            }

                            usr.Save();
                            if (usr.IsDirty)
                                throw new ProviderException("Unable to update failure count.");
                        }
                    }
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateFailureCount");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            
        }


        //
        // CheckPassword
        //   Compares password values based on the MembershipPasswordFormat.
        //

        private bool CheckPassword(string password, string dbpassword)
        {
            string pass1 = password;
            string pass2 = dbpassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Encrypted:
                    pass2 = UnEncodePassword(dbpassword);
                    break;
                case MembershipPasswordFormat.Hashed:
                    pass1 = EncodePassword(password);
                    break;
                default:
                    break;
            }

            if (pass1 == pass2)
            {
                return true;
            }

            return false;
        }


        //
        // EncodePassword
        //   Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        //

        private string EncodePassword(string password)
        {
            string encodedPassword = password;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encodedPassword =
                      Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    HMACSHA1 hash = new HMACSHA1();
                    hash.Key = HexToByte(encryptionKey);
                    encodedPassword =
                      Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
                    break;
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return encodedPassword;
        }


        //
        // UnEncodePassword
        //   Decrypts or leaves the password clear based on the PasswordFormat.
        //

        private string UnEncodePassword(string encodedPassword)
        {
            string password = encodedPassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password =
                      Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    throw new ProviderException("Cannot unencode a hashed password.");
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return password;
        }

        //
        // HexToByte
        //   Converts a hexadecimal string to a byte array. Used to convert encryption
        // key values from the configuration.
        //

        private byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }


        //
        // MembershipProvider.FindUsersByName
        //

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {

            UserCollection usrs = new UserCollection();
            SubSonic.Query qry = new Query(User.Schema);
            qry.AddWhere(User.Columns.Username, SubSonic.Comparison.Like, usernameToMatch);
            qry.ORDER_BY(User.Columns.Username, "ASC");

            MembershipUserCollection users = new MembershipUserCollection();


            try
            {
                usrs.LoadAndCloseReader(qry.ExecuteReader());

                totalRecords = usrs.Count;

                if (totalRecords <= 0) { return users; }

                users = PageUsers(usrs, pageIndex, pageSize);

            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersByUsername");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return users;
        }

        //
        // MembershipProvider.FindUsersByEmail
        //

        private MembershipUserCollection PageUsers(LMSData.UserCollection usrs, int pageIndex, int pageSize)
        {
            MembershipUserCollection users = new MembershipUserCollection();

            int counter = 0;
            int startIndex = pageSize * pageIndex;
            int endIndex = startIndex + pageSize - 1;

            foreach (User usr in usrs)
            {
                if (counter >= startIndex)
                {
                    MembershipUser u = GetMembershipUserFromUser(usr);
                    users.Add(u);
                }

                if (counter >= endIndex) { break; }

                counter++;
            }
            return users;

        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            UserCollection usrs = new UserCollection();
            SubSonic.Query qry = new Query(User.Schema);
            qry.AddWhere(User.Columns.Email, SubSonic.Comparison.Like, emailToMatch);
            qry.ORDER_BY(User.Columns.Email, "ASC");

            MembershipUserCollection users = new MembershipUserCollection();

            
            try
            {
                usrs.LoadAndCloseReader(qry.ExecuteReader());

                totalRecords = usrs.Count;

                if (totalRecords <= 0) { return users; }

                users = PageUsers(usrs, pageIndex, pageSize);
				
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersByEmail");

                    throw new ProviderException(exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }

            return users;
        }

        //
        // WriteToEventLog
        //   A helper function that writes exception detail to the event log. Exceptions
        // are written to the event log as a security measure to avoid private database
        // details from being returned to the browser. If a method does not return a status
        // or boolean indicating the action succeeded or failed, a generic exception is also 
        // thrown by the caller.
        //

        private void WriteToEventLog(Exception e, string action)
        {
            EventLog log = new EventLog();
            log.Source = eventSource;
            log.Log = eventLog;

            string message = "An exception occurred communicating with the data source.\n\n";
            message += "Action: " + action + "\n\n";
            message += "Exception: " + e.ToString();

            log.WriteEntry(message);
        }
         
    }
         
}